﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using FastNet.Internal;

namespace FastNet
{
	public sealed class FastServerClient<TUser> : EventArgs
	{
		internal sealed class Credentials
		{
			public Credentials( String username, String password )
			{
				this.Username = username;
				this.Password = password;
			}


			public String Username { get; private set; }
			public String Password { get; private set; }
		}


		internal FastServerClient( FastServer<TUser> server, Credentials credentials, ICommandChannel channel, String cookie )
		{
			Logic.Check<ArgumentNullException>( server != null, "server" );
			Logic.Check<ArgumentNullException>( channel != null, "channel" );
			Logic.Check<ArgumentNullException>( cookie != null, "cookie" );
			Logic.Check<ArgumentNullException>( credentials != null, "credentials" );

			this.Cookie			= cookie;

			m_credentials		= credentials;
			m_server			= server;
			m_channel			= channel;
			m_activity			= DateTime.UtcNow;
			m_lock				= new Object();
			m_data				= new Dictionary<String, Object>();

			ICommunicationObject underlyingChannel = (ICommunicationObject)channel;

			underlyingChannel.Closed += new EventHandler( OnChannelClosed );
		}


		public String Username
		{
			get
			{
				return m_credentials.Username;
			}
		}


		public String Password
		{
			get
			{
				return m_credentials.Password;
			}
		}


		public String Cookie { get; private set; }


		public CommunicationState State
		{
			get
			{
				return ( (ICommunicationObject)m_channel ).State;
			}
		}


		public Dictionary<String, Object> Data
		{
			get
			{
				return m_data;
			}
		}


		public DateTime LastActivityTime
		{
			get
			{
				return m_activity;
			}
		}


		public TUser User { get; set; }

		
		public Boolean Send( FastMessage message )
		{
			Logic.Check<InvalidOperationException>( OperationContext.Current == null, "You are not allowed to send message in an existing operation context." );

			Boolean					success		= false;
			CommunicationException	exception	= null;

			lock ( m_lock )
			{
				if ( this.State == CommunicationState.Opened )
				{
					TrackActivity();

					try
					{
						m_channel.Send( message.GetData() );

						success = true;
					}
					catch ( CommunicationException ex )
					{
						exception = ex;
					}
				}
			}

			if ( exception != null )
			{
				m_server.RaiseCommunicationException( this, exception );
			}

			return success;
		}


		public void Close()
		{
			lock ( m_lock )
			{
				if ( this.State <= CommunicationState.Opened )
				{
					TrackActivity();

					try
					{
						( (ICommunicationObject)m_channel ).Close();
					}
					catch
					{
					}
				}
			}
		}


		internal void TrackActivity()
		{
			m_activity = DateTime.UtcNow;
		}


		private void OnChannelClosed( Object sender, EventArgs e )
		{
			m_server.DisconnectClient( this );

			if ( this.User is IDisposable )
			{
				( (IDisposable)this.User ).Dispose();
			}
		}


		private DateTime							m_activity;
		private readonly ICommandChannel			m_channel;
		private readonly FastServer<TUser>			m_server;
		private readonly Dictionary<String, Object> m_data;
		private readonly Object						m_lock;
		private readonly Credentials				m_credentials;
	}
}
